#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
 blipoc - Blip.pl On Console
 This is part of blipoc - python-blipoc (or in short: pblipoc)

 $Id$
 
 author: Cezary Statkiewicz (cezio [at] thelirium.net)
 website: http://code.google.com/p/blipoc/

 API: http://www.bliplog.pl/blip-api
 version: 0.01a
 
 To use it:
 import pblipoc.api

"""

# some globals

API_VERSION = 0.01
USER_AGENT = 'blipoc - Blip on console'
UPDATE_BODY_LIMIT = 160
URL = 'http://api.blip.pl'
DEBUG = True # to be False in stable version

import base64, types
import simplejson, urllib2, urllib
import datetime, time
from pprint import pprint

class ApiException(Exception):
	pass

class BlipocInputError(ApiException):
	pass

	
class BaseApiObject(object):
	"""
	base blip api class for containers
	
	__fields__ - dictionary {field: field_type_class_or_type} with attributes of this type
	__values__ - dictionary with object values
	
	get_json() - method for getting json string for class instance
	
	parse_json(data, raw_json = False) - method for setting attributes from json data, if raw_json, then it will be parsed by simplejson.loads

	"""
	__fields__ = {}
	
	def __init__(self, *args, **kwargs):
		self.__values__ = None
		self.credentials = None
		if args:
			try:
				self.parse_json(args[0])
			except Exception, e:
				if DEBUG:
					print args[0]
					print '%s'%e
				
			
		
	def set_credentials(self, username, password):
		"""
		set username and password 
		"""
		self.credentials = (username, password,)
		
	def get_json(self):
		data = {}
		for f in self.__fields__:
			# getting field value
			try:
				field = getattr( self, f)
			except AttributeError:
				# maybe if it's required field it shoudl raise an exception?
				break
			if isinstance(field, BaseApiObject):
				data[f] = field.get_json()
			else:
				data[f] = field
		return simplejson.dumps(data)
		
	def parse_json(self, data, raw_json = False):
		if raw_json:
			data = simplejson.loads(data)
		data = self._parse_json(data)
		for f in data:
			if DEBUG:
				print 'setting self.%s = %s'%( f, data[f])
			setattr(self, f, data[f])
		self.__values__ = data
		
	def _parse_json(self, data):
		#pprint(data)
		d = {}
		if not isinstance(data, dict):
			raise ApiException('Nieprawidłowe dane - spodziewano się słownika, otrzymano %s'%type(data))
		for f in self.__fields__:
			try:
				field = data[f]
			except KeyError:
				if DEBUG:
					print 'missing key', f
				continue
			try:
				value = self.__fields__[f](field)
			except Exception, e:
				raise ApiException('Błąd walidacji typu dla obiektu %s: atrybut %s %s'%( self.__class__.__name__, f, e))
			
			d[f] = value
		return d

	def _print(self):
		"""
		TODO: rewite!!!
		
		formated object representation
		
Class1:
	attr1: value
	class_attr:
	
		Class2:
			attr2: value
			attr3: value
		
		"""
		out = ''
		for f in self.__fields__:
			try:
				if isinstance(getattr(self, f), BaseApiObject):
					tmp = getattr(self, f)._print().split()
					out +='\t%s\n:'%f
					out +='\n\t'.join([ '\t%s'%l for l in  tmp])
				else:
					out += '\t%s:%s\n'%(f, getattr(self, f))
			except AttributeError:
				continue
		#####
		#out = '\n'.join( [ '\t%s : %s'%( k, getattr(self, k, '') )
		#					for k in self.__fields__  ])
		
		out = '%s: \n%s\n'%(self.__class__.__name__, out)
		return out
		
	__str__ = _print
	__repr__ = _print

class Picture(BaseApiObject):
	__fields__ = {'id': int,
					'url': str}
					
class Transport(BaseApiObject):
	__fields__ = {'id': int,
	
					'name': str}

class _Update(BaseApiObject):
	"""
	stupid hack for User.current_status recurrency
	
	"""
	__fields__ = { 'id': int,
					'body': str,
					'type': str,
					'created_at': str,
					'created_at_epoch': float,
					'transport': Transport,
					'attached_pictures': Picture,}
				
					
class User(BaseApiObject):
	__fields__ = {'id': int,
				'login': str,
				'avatar': Picture,
				'color_links': str,
				'color_background': str,
				'current_status': _Update}
				
	def friends(self):
		"""
		get list of user's friends
		"""
		r = Request(self.credentials, '%s/friends/'%(URL), 'GET', None)
		data = r.do_request()
		data = simplejson.loads(data)
		friends = []
		for d in data:
			#print
			#pprint(d)
			u = User()
			u.parse_json(d)
			friends.append(u)
		return friends
			
		
	def users(self, user= None):
		"""
		get user data for user
		if user is not provided, api will return current user
		
		"""
		if user:
			r = Request(self.credentials, '%s/users/%s'%(URL, user), 'GET', None)
		else:
			r = Request(self.credentials, '%s/users/'%(URL), 'GET', None)
			
		data = r.do_request()
		u = User()
		u.parse_json(data, True)
		return u
	
class Update(BaseApiObject):
	__fields__ = { 'id': int,
					'body': str,
					'type': str,
					'src_user': User,
					'dst_user': User,
					'created_at': str,
					'created_at_epoch': float,
					'transport': Transport,
					'attached_pictures': Picture,}
	
	def dashboard(self, update_id = None):
		"""
		retrieves user's statuses. 
		if update_id provided, it will recieve statuses made after update_id
		"""
		if update_id:
			r = Request(self.credentials, '%s/updates/poll/%s'%(URL, update_id), 'GET', None)
		else:
			r = Request(self.credentials, '%s/updates/poll/'%(URL), 'GET', None)
		data = r.do_request()
		return simplejson.loads(data)
    
		#print
		#print data
		#return self.parse_json(data, raw_json = True)

		
	def get(self, update_id):
		"""
		get specified status
		"""
		r = Request(self.credentials, '%s/updates/%s'%(URL, update_id), 'GET', None)
		data = r.do_request()
		self.parse_json(data, raw_json = True)
		return data
		
		
	def set(self, status):
		"""
		set new status
		
		"""
		if len(status)> UPDATE_BODY_LIMIT:
			raise BlipocInputError('status jest dłuższy niż dopuszczalna wielkość: (%s > %s)'%(len(status), UPDATE_BODY_LIMIT) )
		r = Request(self.credentials, 
				'%s/updates'%(URL), 
				'POST', 
				urllib.urlencode( {'body': status}))
		data = r.do_request()
		#self.parse_json(data, raw_json = True)
		return data
	

class Request(object):
	"""
	request object for api.blip.pl
	"""
	def __init__(self, credentials, url, method, data):
		self.credentials = credentials
		self.url = url
		self.method = method
		self.data = data
		self._debug = DEBUG
		
	def do_request(self):
		if self._debug:
			self.__print_debug('Requesting url: %s'%self.url)
		request = urllib2.Request(self.url)
		if self.credentials:
			request.add_header('Authorization', 
								'Basic %s'%( base64.b64encode('%s:%s'%self.credentials )))
		request.add_header('User-Agent', USER_AGENT)
		request.add_header('X-blip-api', '%s'%API_VERSION)
		request.add_header('Accept', 'application/json')
		request.add_header('Pragma', 'no-cache')
		
		if self.data:
			request.add_data(self.data)
			request.add_header('Content-Type', 'application/x-www-form-urlencoded')
		try:
			response = urllib2.urlopen(request)
		except urllib2.HTTPError, e:
			# 201 - Created:
			
			data = e.read()
			if e.code == 201:
				self.__print_debug(data)
				return True
			self.__print_debug('Requested url: %s'%self.url)
			self.__print_debug('error!!!!\nHTTP %s\n%s\n\n%s'%(e.code, e.headers, data ))
			return data
		data = response.read()
		if self._debug:
			self.__print_debug(data)
			#self.__print_debug('%s'%(response.get_info()))
		return data
		
	def __print_debug(self, data):
		print '>>> pblipoc debug', time.ctime()
		print
		if isinstance(data, str):
			print data
		else:
			pprint(data)
		print
		
if __name__ == '__main__':

	import sys
	if len(sys.argv) != 3:
		sys.exit('Usage: __init__.py login haslo')
	username = sys.argv[1]
	password = sys.argv[2]
	u = Update()

	u.set_credentials(username, password)
	#pprint(u.dashboard())
	#u.dashboard(140000) # nie działa
	#sys.exit()
	#u.get(140000)
	u.set('how do you handle f')

	u = User()
	
	u.set_credentials(username, password)
	pprint(u.friends())
	
	pprint(friends)
	pprint(u.users())
	

#
# $Id$
#